/* Copyright (C) 2011 clueless <clueless.mmopeon@gmail.com>
 *
 * This file is part of MMO Peon.
 *
 * MMO Peon is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * MMO Peon is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with MMO Peon.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "selectpanel.h"
#include "application.h"
#include "dllinjection.h"
#include <windows.h>
#include <wx/stdpaths.h>

SelectionPanel::SelectionPanel(wxWindow* parent) : wxPanel(parent)
{
    // Creating the two process lists
    closedProcesses = new wxListCtrl(this, IdClosedListCtrl, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL);
    openProcesses = new wxListCtrl(this, IdOpenListCtrl, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL);

    // Add columns to the lists
    wxListItem itemCol;
    itemCol.SetText(wxT("Window Title"));
    closedProcesses->InsertColumn(0, itemCol);
    openProcesses->InsertColumn(0, itemCol);
    itemCol.SetText(wxT("PID"));
    closedProcesses->InsertColumn(1, itemCol);
    openProcesses->InsertColumn(1, itemCol);
    closedProcesses->SetColumnWidth(0, 120);
    openProcesses->SetColumnWidth(0, 120);


    // Creating the two buttons
    openButton  = new wxButton(this, IdOpenProcessButton, wxT("Open >>"));
    closeButton = new wxButton(this, IdCloseProcessButton, wxT("<< Close"));
    openButton->Disable();
    closeButton->Disable();

    // Create a new grid for the layout
    wxFlexGridSizer *sizer = new wxFlexGridSizer(3, 2, 5, 5);
    sizer->AddGrowableCol(0, 1);
    sizer->AddGrowableCol(1, 1);
    sizer->AddGrowableRow(1, 1);

    // Add labels to layout
    sizer->Add(new wxStaticText(this, wxID_ANY, wxT("Closed")), 0, wxALIGN_CENTER | wxTOP, 5);
    sizer->Add(new wxStaticText(this, wxID_ANY, wxT("Open")), 0, wxALIGN_CENTER | wxTOP, 5);

    // Add list lists to layout
    sizer->Add(closedProcesses, 1, wxEXPAND, 0);
    sizer->Add(openProcesses, 1, wxEXPAND, 0);

    // Add buttons to layout
    sizer->Add(openButton, 1, wxEXPAND | wxBOTTOM, 5);
    sizer->Add(closeButton, 1, wxEXPAND | wxBOTTOM, 5);

    SetSizer(sizer);

    // Create a new timer to update the process lists
    timer = new wxTimer(this, IdProcessTimer);
    timer->Start(250);

    // Set up event handlers
    SetEventHandlers();
}

SelectionPanel::~SelectionPanel()
{
    timer->Stop();
}

void SelectionPanel::SetEventHandlers()
{
    // Handle list select events
    Connect(IdOpenListCtrl, wxEVT_COMMAND_LIST_ITEM_SELECTED, wxCommandEventHandler(SelectionPanel::OnSelectChanged));
    Connect(IdOpenListCtrl, wxEVT_COMMAND_LIST_ITEM_DESELECTED, wxCommandEventHandler(SelectionPanel::OnSelectChanged));
    Connect(IdClosedListCtrl, wxEVT_COMMAND_LIST_ITEM_SELECTED, wxCommandEventHandler(SelectionPanel::OnSelectChanged));
    Connect(IdClosedListCtrl, wxEVT_COMMAND_LIST_ITEM_DESELECTED, wxCommandEventHandler(SelectionPanel::OnSelectChanged));

    // Handle button clicks
    Connect(IdOpenProcessButton, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SelectionPanel::OnOpenProcess));
    Connect(IdCloseProcessButton, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(SelectionPanel::OnCloseProcess));

    // Handle timer pulses
    Connect(IdProcessTimer, wxEVT_TIMER, wxCommandEventHandler(SelectionPanel::OnTimer));
}

void SelectionPanel::OnSelectChanged(wxCommandEvent& WXUNUSED(event))
{
    if(openProcesses->GetSelectedItemCount() == 0)
        closeButton->Disable();
    else
        closeButton->Enable();

    if(closedProcesses->GetSelectedItemCount() == 0)
        openButton->Disable();
    else
        openButton->Enable();
}

void SelectionPanel::OnOpenProcess(wxCommandEvent& WXUNUSED(event))
{
    long closedIndex = closedProcesses->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);

    wxListItem newItem;
    long openIndex = openProcesses->InsertItem(newItem);

    openProcesses->SetItem(openIndex, 0, closedProcesses->GetItemText(closedIndex, 0));
    openProcesses->SetItem(openIndex, 1, closedProcesses->GetItemText(closedIndex, 1));
    closedProcesses->DeleteItem(closedIndex);

    //TODO: inject dll
    wxStandardPaths sp;
    wxString dllPath = sp.GetDataDir() + wxT("\\mmopeon.dll");
    DllInject(openProcesses->GetItemText(openIndex, 1).c_str().AsCharBuf(), dllPath.c_str().AsCharBuf());
}

void SelectionPanel::OnCloseProcess(wxCommandEvent& WXUNUSED(event))
{
    long openIndex = openProcesses->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);

    wxListItem newItem;
    long closedIndex = closedProcesses->InsertItem(newItem);

    closedProcesses->SetItem(closedIndex, 0, openProcesses->GetItemText(openIndex, 0));
    closedProcesses->SetItem(closedIndex, 1, openProcesses->GetItemText(openIndex, 1));
    openProcesses->DeleteItem(openIndex);

    //TODO: eject DLL
    wxStandardPaths sp;
    wxString dllPath = sp.GetDataDir() + wxT("\\mmopeon.dll");
    DllEject(closedProcesses->GetItemText(closedIndex, 1).c_str().AsCharBuf(), dllPath.c_str().AsCharBuf());
}

wxVector<RomProcessInfo> SelectionPanel::CreateProcessList()
{
    wxVector<RomProcessInfo> procList;
    HWND romWindow = NULL;
    wchar_t title[256];

    // Put all active processes in the procList
    while ((romWindow = FindWindowEx(NULL, romWindow, L"Radiant Arcana", NULL)) != NULL)
    {
        DWORD pid;
        RomProcessInfo proc;

        GetWindowThreadProcessId(romWindow, &pid);
        GetWindowText(romWindow, title, 256);

        proc.pid << pid;
        proc.title = title;

        procList.push_back(proc);
    }

    return procList;
}

void SelectionPanel::OnTimer(wxCommandEvent& WXUNUSED(event))
{
    wxVector<RomProcessInfo> procList = CreateProcessList();
    UpdateOpenProcesses(procList);
    UpdateClosedProcesses(procList);
}

void SelectionPanel::UpdateOpenProcesses(wxVector<RomProcessInfo>& procList)
{
    for (int i = 0; i < openProcesses->GetItemCount(); i++)
    {
        // Verify if the process is also in procList
        bool processExists = false;
        for (wxVector<RomProcessInfo>::iterator it = procList.begin(); it != procList.end(); it++)
        {
            if (it->pid == openProcesses->GetItemText(i, 1))
            {
                processExists = true;
                break;
            }
        }

        if (!processExists)
        {
            // Delete it from open process list if its not open anymore
            // TODO: Destruct anything that might have been created (tabs, connection etc)
            openProcesses->DeleteItem(i);
            i--;
        }
        else
        {
            // process is open, remove it from the process list so it doesnt get added to closed process list
            for (wxVector<RomProcessInfo>::iterator it = procList.begin(); it != procList.end(); it++)
            {
                if (it->pid == openProcesses->GetItemText(i, 1))
                {
                    procList.erase(it);
                    break;
                }
            }
        }
    }
}

void SelectionPanel::UpdateClosedProcesses(wxVector<RomProcessInfo>& procList)
{
    // Delete processes that arent open anymore
    for (int i = 0; i < closedProcesses->GetItemCount(); i++)
    {
        //wxMessageBox( _("Looping over closed processes"), _("Info"), wxOK | wxICON_INFORMATION, this);
        bool processExists = false;
        for (wxVector<RomProcessInfo>::iterator it = procList.begin(); it != procList.end(); it++)
        {
            if (it->pid == closedProcesses->GetItemText(i, 1))
            {
                //wxMessageBox( _("Comparing ") + it->pid + _(" to ") + closedProcesses->GetItemText(i, 1), _("Info"), wxOK | wxICON_INFORMATION, this);
                processExists = true;
                break;
            }
        }

        if (!processExists)
        {
            closedProcesses->DeleteItem(i);
            i--;
        }
    }

    // Add processes that arent in the list yet
    for (wxVector<RomProcessInfo>::iterator it = procList.begin(); it != procList.end(); it++)
    {
        bool isListed = false;
        for (int i = 0; i < closedProcesses->GetItemCount(); i++)
        {
            if (it->pid == closedProcesses->GetItemText(i, 1))
            {
                isListed = true;
                break;
            }
        }

        if (!isListed)
        {
            wxListItem newItem;
            long index = closedProcesses->InsertItem(newItem);
            closedProcesses->SetItem(index, 0, it->title);
            closedProcesses->SetItem(index, 1, it->pid);
        }
    }
}

